"""
语法分析器文件
"""
from tokens import *
from error import *
from ast_node import *

"""
语法解析结果类
"""


class ParserResult(object):
    """
    node: 语法树解析出的 Node, 如 NumberNode, BinOpNode, UnaryOpNode
    """

    def __init__(self):
        self.error = None
        self.node = None

    def success(self, node: Node):
        self.node = node
        return self

    def failure(self, error):
        self.error = error
        return self

    def register(self, res):
        """
        传递错误, 如果抽象语法树上某个节点出错, 可以将错误传递到树根
        :param res:
        :return: 如果是ParserResult,返回node,如果是普通node,直接返回
        """
        if isinstance(res, ParserResult):
            if res.error:
                self.error = res.error
            return res.node
        return res


"""
语法解析器
"""


class Parser(object):
    def __init__(self, tokens: list):
        self.current_tok: Token = None  # 当前 token
        self.tokens = tokens  # 词法分析器解析的 tokens
        self.tok_idx = -1  # 当前数组下标
        self.advance()  # 获取下一个token

    def advance(self):
        """
        获取下一个 token
        :return:
        """
        self.tok_idx += 1
        if self.tok_idx < len(self.tokens):
            self.current_tok = self.tokens[self.tok_idx]
        return self.current_tok

    def parse(self) -> ParserResult:
        """
        构建抽象语法树
        :return:
        """
        # 第一个非终结符是 expr, 从 expr 开始解析
        res = self.expr()

        if not res.error and self.current_tok.type != TT_EOF:
            # 报错
            return res.failure(
                InvalidSyntaxError(self.current_tok.pos_start,
                                   self.current_tok.pos_end,
                                   "期望 '+', '-', '*', '/'"))
        return res

    """
        三个非终结符的处理
    """

    def expr(self) -> ParserResult:
        """
        expr => term ((PLUS | MINUS) term)*
        :return:
        """
        # 相同的逻辑, 一起处理了
        return self.bin_op(self.term, (TT_PLUS, TT_MINUS))

    def term(self) -> ParserResult:
        """
        term => factor(( MUL | DIV) factor)*
        :return:
        """
        # 相同的逻辑, 一起处理了
        return self.bin_op(self.factor, (TT_MUL, TT_DIV))

    def bin_op(self, func, ops) -> ParserResult:
        res = ParserResult()
        left = res.register(func())
        while self.current_tok.type in ops:
            # 获取中间那个运算符
            op_token = self.current_tok
            # 进入下一个 token
            res.register(self.advance())
            # 获取右边的操作数
            right = res.register(func())
            left = BinOpNode(left, op_token, right)
        return res.success(left)

    def factor(self) -> ParserResult:
        """
        factor => INT | FLOAT # 浮点数或者整数
               => ( PLUS | MINUS ) factor # 正数或者是负数
               => LPAREN expr RPAREN # 括号
        :return:
        """
        res = ParserResult()
        token = self.current_tok

        # 是普通的整数或者浮点数, 直接返回了
        if token.type in (TT_INT, TT_FLOAT):
            # 规范这样写, 实际上这里可以直接 self.advance()
            res.register(self.advance())
            return res.success(NumberNode(token))
        # 正数或者是负数
        elif token.type in (TT_PLUS, TT_MINUS):
            # 比如说 -1 , self.advance() = 1
            res.register(self.advance())
            # 将这个 1 递归进行处理, 看看文法有没有错
            factor = res.register(self.factor())
            if res.error:
                return res
            # 无错返回
            return res.success(UnaryOpNode(token, factor))
        elif token.type == TT_LPAREN:
            res.register(self.advance())
            expr = res.register(self.expr())
            if res.error:
                return res
            if self.current_tok.type == TT_RPAREN:
                res.register(self.advance())
                return res.success(expr)
            else:
                # 报错
                return res.failure(
                    InvalidSyntaxError(self.current_tok.pos_start,
                                       self.current_tok.pos_end,
                                       "期望 ')'"))
        # 报错
        return res.failure(
            InvalidSyntaxError(self.current_tok.pos_start,
                               self.current_tok.pos_end,
                               "期望 'INT' 或者 'FLOAT'"))
